<!-- *@description:  -->
<!-- *@Author: M.佑先生 -->
<!-- *@Date: 2025-07-11 09:45:11 -->
<template>
	<div class="base-skill-canvas">
		<div class="mainBox">
			<canvas ref="canvas" :width="canvasWidth" :height="canvasHeight"></canvas>
			<div
				v-for="(item, idx) in nodes"
				:key="item.id"
				class="node-box"
				:style="getNodeStyle(idx)"
			>
				<div class="node-label" v-if="!item.twoCol">{{ item.label }}</div>
				<div class="node-label two-col-label" v-else>
					<span v-for="(col, colIdx) in item.label" :key="colIdx">
						<span v-for="(txt, rowIdx) in col" :key="rowIdx">{{ txt }}</span>
					</span>
				</div>
			</div>
		</div>
		<div class="title-box">基础能力</div>
	</div>
</template>

<script setup>
import { ref, onMounted, watch, nextTick, onUnmounted, computed } from 'vue';

// 设计稿基准尺寸（为响应式设计提供参考）
const DESIGN_WIDTH = 1920;
const DESIGN_HEIGHT = 195;

// 响应式窗口尺寸状态
const windowWidth = ref(window.innerWidth);
const windowHeight = ref(window.innerHeight);

// 计算 canvas 的宽度（根据窗口尺寸缩放）
const canvasWidth = computed(() => {
	const scale = windowWidth.value / DESIGN_WIDTH; // 计算缩放比例
	return Math.round(880 * scale); // 880px 是设计稿中的宽度
});

// 计算 canvas 的高度（根据窗口尺寸缩放）
const canvasHeight = computed(() => {
	const scale = windowWidth.value / DESIGN_WIDTH; // 计算缩放比例
	return Math.round(DESIGN_HEIGHT * scale); // 设计稿中的高度
});

// 计算节点半径（用于粒子动画的大小）
const nodeRadius = computed(() => {
	const scale = windowWidth.value / DESIGN_WIDTH;
	return Math.round(30 * scale); // 30px 是设计稿中的节点半径
});

// 计算节点在 canvas 中的 Y 坐标
const nodeY = computed(() => {
	return (
		canvasHeight.value - nodeRadius.value - Math.round(10 * (windowWidth.value / DESIGN_WIDTH))
	);
});

// 节点数据
const nodes = ref([
	{ id: 1, label: '集成平台' },
	{ id: 2, label: '数据平台' },
	{ id: 3, label: 'AI平台' },
	{ id: 4, label: '网格平台' },
	{ id: 5, label: [['合信', '融通'], '台平'], twoCol: true },
	{ id: 6, label: 'GIS平台' },
	{ id: 7, label: [['一权', '统授'], '台平'], twoCol: true }
]);

// Canvas 元素引用
const canvas = ref(null);

// 粒子动画的参数
const particles = ref([]);
const particleSpeed = 0.0012; // 粒子运动的速度
const particleRadius = computed(() => {
	const scale = windowWidth.value / DESIGN_WIDTH;
	return Math.round(5 * scale); // 粒子的半径，响应式
});

// 计算每个节点的样式，包括位置和大小
function getNodeStyle(idx) {
	const n = nodes.value.length; // 获取节点数量
	const gap = canvasWidth.value / (n + 1); // 计算节点之间的间隔
	const x = gap * (idx + 1); // 计算当前节点的 x 坐标

	// 计算 U 型弧度，确保两端较高，中间较低的效果
	const maxOffset = 40 * (windowWidth.value / DESIGN_WIDTH); // 最大偏移量
	const t = idx / (n - 1); // 归一化位置值（0~1）
	const offset = maxOffset * Math.pow(2 * t - 1, 2); // 计算 U 型曲线的偏移量（高斯函数）

	// 节点框的大小和位置计算
	const scale = windowWidth.value / DESIGN_WIDTH;
	const nodeBoxWidth = Math.round(80 * scale); // 节点宽度
	const nodeBoxHeight = Math.round(90 * scale); // 节点高度
	const upwardOffset = 28 * (windowWidth.value / DESIGN_WIDTH); // 上升偏移量，控制节点位置的垂直偏移
	return {
		left: `${x - nodeBoxWidth / 2}px`,
		top: `${nodeY.value - nodeBoxHeight / 2 - offset - upwardOffset}px`, // 顶点的位置，整体往上偏移
		width: `${nodeBoxWidth}px`,
		height: `${nodeBoxHeight}px`
	};
}

// 初始化粒子动画
function initParticles() {
	particles.value = nodes.value.map((_, idx) => ({
		t: Math.random(), // 每个粒子随机初始进度，错开启动
		delay: Math.random() * 0.5 // 可选：初始延迟
	}));
}

// 绘制线条和粒子
function drawLinesAndParticles() {
	if (!canvas.value) return;

	const ctx = canvas.value.getContext('2d');
	ctx.clearRect(0, 0, canvasWidth.value, canvasHeight.value); // 清空画布
	ctx.save();

	const lineCount = nodes.value.length; // 获取节点数量
	const gap = canvasWidth.value / (lineCount + 1); // 计算每条线的间隔
	const startX = canvasWidth.value / 2; // 起始点 x 坐标
	const startY = Math.round(17 * (windowWidth.value / DESIGN_WIDTH)); // 起始点 y 坐标
	const controlYOffset = Math.round(60 * (windowWidth.value / DESIGN_WIDTH)); // 控制点的 Y 偏移量

	nodes.value.forEach((item, idx) => {
		const endX = gap * (idx + 1); // 终点的 x 坐标
		const endY = nodeY.value - nodeRadius.value; // 终点的 y 坐标
		const controlX = (startX + endX) / 2; // 贝塞尔曲线控制点的 X 坐标（中点）
		const controlY = Math.min(startY, endY) - controlYOffset; // 控制点的 Y 坐标，决定曲线弯曲程度

		// 画线：使用二次贝塞尔曲线
		const grad = ctx.createLinearGradient(startX, startY, endX, endY); // 创建渐变色
		grad.addColorStop(0, '#01f1ff00'); // 起始点颜色
		grad.addColorStop(0.25, '#027DFB'); // 中间点颜色
		grad.addColorStop(0.67, '#01F1FF'); // 中间点颜色
		grad.addColorStop(1, '#01f1ff00'); // 终点颜色

		ctx.beginPath();
		ctx.moveTo(startX, startY); // 起点
		ctx.quadraticCurveTo(controlX, controlY, endX, endY); // 画二次贝塞尔曲线
		ctx.strokeStyle = grad; // 使用渐变色
		ctx.lineWidth = Math.round(2 * (windowWidth.value / DESIGN_WIDTH)); // 线宽
		ctx.shadowColor = '#00faff'; // 设置阴影颜色
		ctx.shadowBlur = Math.round(8 * (windowWidth.value / DESIGN_WIDTH)); // 阴影模糊
		ctx.stroke();

		// 绘制粒子动画
		const p = particles.value[idx];
		if (p) {
			const t = p.t; // 获取当前粒子的进度
			// 使用二次贝塞尔曲线公式计算粒子的位置
			const x = (1 - t) * (1 - t) * startX + 2 * (1 - t) * t * controlX + t * t * endX;
			const y = (1 - t) * (1 - t) * startY + 2 * (1 - t) * t * controlY + t * t * endY;

			// 绘制粒子
			ctx.save();
			const gradDot = ctx.createRadialGradient(x, y, 0, x, y, particleRadius.value); // 创建粒子的渐变色
			gradDot.addColorStop(0, '#fff');
			gradDot.addColorStop(0.3, '#00faff');
			gradDot.addColorStop(1, 'rgba(0,255,255,0)');
			ctx.beginPath();
			ctx.arc(x, y, particleRadius.value, 0, Math.PI * 2); // 绘制圆形粒子
			ctx.fillStyle = gradDot;
			ctx.shadowColor = '#00faff'; // 设置粒子阴影
			ctx.shadowBlur = Math.round(16 * (windowWidth.value / DESIGN_WIDTH)); // 阴影模糊
			ctx.globalAlpha = 0.85; // 粒子的透明度
			ctx.fill();
			ctx.restore();
		}
	});

	ctx.restore();
}

let animId = null;
// 动画函数：更新粒子位置并重新绘制
// function animate() {
// 	particles.value.forEach((p) => {
// 		p.t += particleSpeed; // 更新粒子的进度
// 		if (p.t > 1) p.t = 0; // 粒子进度重置
// 	});
// 	drawLinesAndParticles(); // 重新绘制线条和粒子
// 	animId = requestAnimationFrame(animate); // 请求下一帧
// }

function animate() {
	particles.value.forEach((p) => {
		p.t -= particleSpeed; // 更新粒子的进度，反向更新
		if (p.t < 0) p.t = 1; // 粒子进度重置为 1（反向运动）
	});
	drawLinesAndParticles(); // 重新绘制线条和粒子
	animId = requestAnimationFrame(animate); // 请求下一帧
}

// 处理窗口尺寸变化
function handleResize() {
	windowWidth.value = window.innerWidth;
	windowHeight.value = window.innerHeight;

	if (canvas.value) {
		canvas.value.width = canvasWidth.value; // 更新 canvas 宽度
		canvas.value.height = canvasHeight.value; // 更新 canvas 高度
		drawLinesAndParticles(); // 重新绘制
	}
}

// 防抖函数，避免频繁触发 resize 事件
function debounce(func, wait) {
	let timeout;
	return function executedFunction(...args) {
		const later = () => {
			clearTimeout(timeout);
			func(...args);
		};
		clearTimeout(timeout);
		timeout = setTimeout(later, wait);
	};
}

const debouncedResize = debounce(handleResize, 100);

onMounted(() => {
	initParticles(); // 初始化粒子
	drawLinesAndParticles(); // 绘制初始图形
	animId = requestAnimationFrame(animate); // 启动动画

	// 监听窗口大小变化
	window.addEventListener('resize', debouncedResize);
});

watch(nodes, async () => {
	await nextTick();
	initParticles();
	drawLinesAndParticles();
});

// 监听窗口尺寸变化，重新计算节点位置
watch([windowWidth, windowHeight], () => {
	nextTick(() => {
		drawLinesAndParticles();
	});
});

onUnmounted(() => {
	if (animId) cancelAnimationFrame(animId); // 清除动画
	window.removeEventListener('resize', debouncedResize); // 移除事件监听
});
</script>

<style lang="scss" scoped>
.base-skill-canvas {
	width: 880px;
	height: 198px;
	margin-bottom: 116px;
	position: relative;
	background: url('@/assets/img/basenl.png') no-repeat center center;
	background-size: 100% 100%;

	// 响应式适配
	//@media screen and (max-width: 1920px) {
	//	width: calc(880 * 100vw / 1920);
	//	height: calc(175 * 100vw / 1920);
	//}

	.title-box {
		width: 141px;
		height: 41px;
		position: absolute;
		left: 50%;
		bottom: -16px;
		text-align: center;
		line-height: 41px;
		transform: translateX(-50%);
		background: url('@/assets/img/titleBg.png') no-repeat center center;
		background-size: 100% 100%;
		color: #fff;
		font-size: 20px;
		font-weight: bold;
		border-radius: 8px;
		z-index: 3;
		cursor: pointer;
		letter-spacing: 4px;
	}
	.mainBox {
		position: absolute;
		top: -64px;
		width: 100%;
		height: 100%;
	}
	.node-box {
		padding-top: 10px;
		box-sizing: border-box;
		position: absolute;
		width: 74px;
		height: 108px;
		background: url('@/assets/img/base1.png') no-repeat center center;
		background-size: 100% 100%;
		// background: rgba(0, 255, 255, 0.08);
		// border: 2px solid #00faff;
		// border-radius: 50%;
		// box-shadow: 0 0 16px #00faff, 0 0 32px #00faff44;
		display: flex;
		align-items: center;
		justify-content: center;
		.node-label {
			color: #fff;
			font-size: 12px;
			text-align: center;
			writing-mode: vertical-rl;
			text-orientation: mixed;
			letter-spacing: 2px;
			font-weight: bold;
			text-shadow: 0 0 8px #00faff;
			white-space: pre-line;
			word-break: keep-all;
			overflow: hidden;
			max-height: 90px;
		}
		.two-col-label {
			display: flex;
			flex-direction: row;
			justify-content: center;
			align-items: flex-start;
			gap: 2px;
			span {
				display: flex;
				flex-direction: column;
				align-items: center;
				font-size: 12px;
				font-weight: bold;
				color: #fff;
				text-shadow: 0 0 8px #00faff;
				letter-spacing: 2px;
			}
		}
	}
}
</style>
